package com.anji.hyperneat.modular;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import org.jgap.Chromosome;

import com.anji.hyperneat.nd.NDActivatorArray;
import com.anji.hyperneat.nd.NDFloatArray;
import com.anji.neat.Evolver;
import com.anji.tug.Objective;
import com.anji.tug.TugManager;
import com.anji.util.Properties;

public class TugTestFF2 extends ModularHnTugFf {

	/**
	 * 
	 */
	private static final long serialVersionUID = 9035016590795735381L;

	@Override
	public void init(Properties props) {
		super.init(props);
		initializeTugManager(props);
	}
	
	@Override
	public void initializeTugManager(Properties props) {
		HashMap<String, Objective> objectives = new HashMap<String, Objective>(4);
		
		objectives.put("capturedBLCount", new Objective(1.0f, 0.6f, 0.6f, "capturedBLCount"));
		objectives.put("capturedURCount", new Objective(1.0f, 0.6f, 0.6f, "capturedURCount"));
		objectives.put("capturedBRCount", new Objective(1.0f, 0.6f, 0.6f, "capturedBRCount"));
		objectives.put("capturedULCount", new Objective(1.0f, 0.6f, 0.6f, "capturedULCount"));
		
		tugManager = new TugManager(objectives);
	}

	@Override
	public int getMaxFitnessValue() {
		return 1000;
	}

	@Override
	protected int evaluate(Chromosome genotype, NDActivatorArray substrate, int evalThreadIndex) {
		int x = Math.random() * 2 > 1 ? 2 : 1;
		int y = Math.random() * 2 > 1 ? 2 : 1;
		
		NDFloatArray inputs = new NDFloatArray(new int[] {4,4,2});
		NDFloatArray outs;
		
		int[][] visited = new int[4][4];
		for (int i = 0; i < visited.length; i++)
			for (int j = 0; j < visited[i].length; j++) 
				visited[i][j] = 0;
		
		int capturedULCount = 0;
		int capturedURCount = 0;
		int capturedBLCount = 0;
		int capturedBRCount = 0;
		
		HashMap<String, Float> metrics = new HashMap<String, Float>(4);
		
		
		for (int time = 0; time < 40; time++) {
			
			inputs.clear();
			inputs.set(1.0f, x, y, 0);
			for (int yd = 0; yd < visited.length; yd++) {
				for (int xd = 0; xd < visited.length; xd++) {
					inputs.set(visited[yd][xd], xd, yd, 1);
				}
			}
			
			outs = substrate.get(0).next(inputs);
			
			Coordinates c = getMaxForDim(outs);
			c = getActualCoordsFromOutputCoords(c, x, y);
			x = c.x;
			y = c.y;
			
			visited[y][x]++;
			
			if (x == 0 && y == 0) capturedULCount++;
			if (x == 3 && y == 0) capturedURCount++;
			if (x == 0 && y == 3) capturedBLCount++;
			if (x == 3 && y == 3) capturedBRCount++;
		}
		
		float target = 4.0f;
		metrics.put("capturedULCount", Math.min(((float)capturedULCount) / target, 1.0f));
		metrics.put("capturedURCount", Math.min(((float)capturedURCount) / target, 1.0f));
		metrics.put("capturedBLCount", Math.min(((float)capturedBLCount) / target, 1.0f));
		metrics.put("capturedBRCount", Math.min(((float)capturedBRCount) / target, 1.0f));
		
		individualMetrics.put(genotype, metrics);
		

		
		float fitness = 0.0f;
		fitness += Math.min(((float)capturedULCount) / target, 1.0f);
		fitness += Math.min(((float)capturedURCount) / target, 1.0f);
		fitness += Math.min(((float)capturedBLCount) / target, 1.0f);
		fitness += Math.min(((float)capturedBRCount) / target, 1.0f);
		fitness /= target;
		
		if (fitness >= 1.0f) 
			genotype.setPerformanceValue(1.0f);
		
		return 0;
	}

	@Override
	protected void scale(int scaleCount, int scaleFactor) {
		// TODO Auto-generated method stub

	}
	
	private Coordinates getMaxForDim(NDFloatArray outputs /*, int zindex */) {
		Coordinates bestCoords = null;
		float current, best = -1;
		
		for (int y = 0; y < 3; y++) {
			for (int x = 0; x < 3; x++) {
				current = outputs.get(x,y,0);
				if (bestCoords == null || current > best) {
					best = current;
					bestCoords = new Coordinates(x, y);
				}
			}
		}
		
		return bestCoords;
	}
	
	private class Coordinates {
		int x, y;
		public Coordinates(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}

	private Coordinates getActualCoordsFromOutputCoords(Coordinates outputCoords, int x, int y) {
		Coordinates actualCoords = new Coordinates(0,0);
		switch (outputCoords.x) {
		case 0:
			actualCoords.x = x - 1;
			break;
		case 1:
			actualCoords.x = x;
			break;
		case 2:
			actualCoords.x = x + 1;
			break;
		}

		switch (outputCoords.y) {
		case 0:
			actualCoords.y = y - 1;
			break;
		case 1:
			actualCoords.y = y;
			break;
		case 2:
			actualCoords.y = y + 1;
			break;
		}

		if (actualCoords.x < 0) actualCoords.x = 0;
		if (actualCoords.x > 3) actualCoords.x = 3;
		if (actualCoords.y < 0) actualCoords.y = 0;
		if (actualCoords.y > 3) actualCoords.y = 3;
		
		return actualCoords;
	}
	
	/**
	 * @param args
	 * @throws IOException 
	 * @throws Throwable 
	 */
	public static void main(String[] args) throws Throwable  {
		try {
			if ( args.length != 1 ) {
				System.exit( -1 );
			}

			// Anji Properties class... file must be in classpath 
			Properties props = new Properties( args[ 0 ] );
            
            String runID = props.getProperty("run.name") + "-" + System.currentTimeMillis();
            String outputDir = props.getProperty("fitness_function.class") + File.separatorChar + runID + File.separatorChar;
            props.setProperty("run.id", ""+runID);
            props.setProperty("output.dir", outputDir);
            
            Evolver evolver = new Evolver();
            
            evolver.init( props );
            evolver.run();
            System.exit( 0 );
		}
		catch ( Throwable th ) {
//			logger.log(Level.SEVERE, th.getMessage());
			throw th;
		}

	}

}
